package org.rabbitmq.mq02.mqServer.core;

import org.rabbitmq.mq02.common.MqException;

/**
 * 该类实现和绑定相关的操作
 //路由规定:
 //routingKey: 只能由 数字 字母(大小写) 下划线 构成,使用.作为分割
 //bindingKey:只能包含 数字 字母 下划线 * #,以 . 作为分割,* #只能作为独立的分段
 */
public class Router {
    //判断消息携带的绑定格式是否正确
    public boolean checkRoutingKey(String routingKey){
        char[] ch = routingKey.toCharArray();
        for(char i:ch){
            if(i>='a' && i<='z') continue;
            if(i>='A' && i<='Z') continue;
            if(i>='0' && i<='9') continue;
            if(i=='.' || i=='_') continue;
            return false;
        }
        return true;
    }

    //判断绑定格式是否正确
    public boolean checkBindingKey(String bindingKey){
        char[] ch = bindingKey.toCharArray();
        for(char c:ch){
            if(c>='A' && c<='Z') continue;
            if(c>='a' && c<='z') continue;
            if(c>='0' && c<='9') continue;
            if(c=='_' || c=='*' || c=='#' || c=='.') continue;
            return false;
        }
        //规定不能让* #相连,即出现以下情况规定不成立:
        // *.#   #.*  #.#
        //以 . 对字符串进行分隔,判断
        String[] s = bindingKey.split("\\.");
        for(int i=0;i<s.length-1;i++){
            if (s[i].equals("*") && s[i+1].equals("#") ||
                s[i].equals("#") && s[i+1].equals("*") ||
                s[i].equals("#") && s[i+1].equals("#")) {
                return false;
            }
        }
        return true;
    }
    //判断bindingKey与routingKey是否匹配成功
    public boolean isRouting(ExchangeType type,String routingKey,String bindingKey) throws MqException {
        //判断当前交换机类型:fanout/topic
        if(type==ExchangeType.FANOUT){
            //匹配到绑定交换机的所有队列
            //直接返回即可
            return true;
        }else if(type==ExchangeType.TOPIC){
            //进行routingKey和BindingKey的匹配判断
            return routingTopic(routingKey,bindingKey);
        }else{
            throw new MqException("[Router] 交换机类型有误 type:"+type);
        }
    }

    /**
     * 规定:rotingKey匹配bindingKey
     *  *:匹配任意单个字符串
     *  #:匹配任意个任意字符串
     * @param routingKey 消息携带的匹配字符串
     * @param bindingKey 交换机和队列的绑定关系
     * @return
     */
    // [测试用例]
    // binding key          routing key         result
    // aaa                  aaa                 true
    // aaa.bbb              aaa.bbb             true
    // aaa.bbb              aaa.bbb.ccc         false
    // aaa.bbb              aaa.ccc             false
    // aaa.bbb.ccc          aaa.bbb.ccc         true
    // aaa.*                aaa.bbb             true
    // aaa.*.bbb            aaa.bbb.ccc         false
    // *.aaa.bbb            aaa.bbb             false
    // #                    aaa.bbb.ccc         true
    // aaa.#                aaa.bbb             true
    // aaa.#                aaa.bbb.ccc         true
    // aaa.#.ccc            aaa.ccc             true
    // aaa.#.ccc            aaa.bbb.ccc         true
    // aaa.#.ccc            aaa.aaa.bbb.ccc     true
    // #.ccc                ccc                 true
    // #.ccc                aaa.bbb.ccc         true
    private boolean routingTopic(String routingKey, String bindingKey) {
        String[] b = bindingKey.split("\\.");
        String[] r = routingKey.split("\\.");
        int n1 = b.length;
        int n2 = r.length;
        int i = 0;
        int j = 0;
        while(i<n1 && j<n2){
            if(b[i].equals("*")){
                //可以匹配routingKey的任意单个字符
                //直接向后走就行:
                i++;
                j++;
            }else if(b[i].equals("#")){
                //匹配routingKey的任意个任意字符
                if(i==n1-1){
                    //当bindingKey的最后一个字符为#时,可以匹配routingKey后面的所有字符串,直接返回true即可:
                    return true;
                }else{
                    //当b的#不是最后一个字符时,就找r之后的字符串中是否有b的下一个字符串的下标,当找不到时,就返回-1:
                    i++;
                    j = checkNext(b[i],r,j);
                    //当在r中找不到b的下一个字符串时,一定匹配失败,直接返回
                    if(j==-1) return false;
                    else{
                        i++;
                        j++;
                    }
                }
            }else{
                //b为普通字符串时
                if(!b[i].equals(r[j])) return false;
                else {
                    i++;
                    j++;
                }
            }
        }
        //b / r有一个已经匹配到结尾了,只有两个都完全匹配完,才算匹配成功
        if(i!=n1 || j!=n2){
            return false;
        }
        return true;
    }
    private int checkNext(String next, String[] r, int j) {
        for(int k=j;k<r.length;k++){
            if(r[k].equals(next)) return k;
        }
        return -1;
    }


}
